import * as THREE from 'three'
import { useEffect } from 'react'
import './App.css'
import * as dat from 'dat.gui'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { Line2 } from 'three/examples/jsm/lines/Line2'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

const App = () => {
  useEffect(() => {
    const scene = new THREE.Scene()

    const axes = new THREE.AxesHelper(200)
    scene.add(axes)

    /** 相机设置 */
    const width = window.innerWidth //窗口宽度
    const height = window.innerHeight //窗口高度
    const k = width / height //窗口宽高比
    const s = 200 //三维场景显示范围控制系数，系数越大，显示的范围越大
    //创建相机对象
    const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 0.1, 1000)
    camera.position.set(200, 200, 500) //设置相机位置
    camera.lookAt(scene.position) //设置相机方向(指向的场景对象)

    /** 创建渲染器对象 */
    const renderer = new THREE.WebGLRenderer()
    renderer.setSize(width, height) //设置渲染区域尺寸
    // renderer.setClearColor(0x888888, 1); //设置背景颜色
    document.body.appendChild(renderer.domElement) //body元素中插入canvas对象

    renderer.shadowMap.enabled = true
    // renderer.render(_scene, _camera); //执行渲染操作

    const controls = new OrbitControls(camera, renderer.domElement)
    controls.update()

    light(scene)

    // 增加地面
    const groundGeom = new THREE.PlaneGeometry(1024, 1024, 2, 2)
    const groundMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff })
    const groundMesh = new THREE.Mesh(groundGeom, groundMaterial)
    groundMesh.receiveShadow = true
    groundMesh.rotation.x = - Math.PI / 2
    groundMesh.position.y = -40
    scene.add(groundMesh)

    const cubeGemo = new THREE.BoxGeometry(15, 15, 15)
    const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0x1890ff })
    const cubeMesh = new THREE.Mesh(cubeGemo, cubeMaterial)
    // 物体可投影
    cubeMesh.castShadow = true
    cubeMesh.position.set(0, 60, 0)
    scene.add(cubeMesh)

    const cubeGemo2 = new THREE.BoxGeometry(15, 15, 15)
    const cubeMaterial2 = new THREE.MeshLambertMaterial({ color: 0x189099 })
    const cubeMesh2 = new THREE.Mesh(cubeGemo2, cubeMaterial2)
    cubeMesh2.castShadow = true
    cubeMesh2.position.set(50, 10, 0)
    scene.add(cubeMesh2)

    // 创建一个顶点缓冲几何体
    let lineGeometry = new LineGeometry()
    const vertices = new Float32Array([
      20,50,0, // 第一个点坐标
      20,20,0, // 第二个点坐标
      // -20,10,0, // 第三个点坐标
      // -20,50,0 // 第四个点坐标
    ])
    lineGeometry.setPositions(vertices)
    // 线条渲染模式
    const material = new LineMaterial({
      color: 0xffffff, //线条颜色
      linewidth: 4, // 线条宽度
    });
    // 把渲染窗口尺寸分辨率传值给材质LineMaterial的resolution属性
    // resolution属性值会在着色器代码中参与计算
    material.resolution.set(window.innerWidth, window.innerHeight);
    // 创建线模型对象   构造函数：Line、LineLoop、LineSegments
    const mesh = new Line2( lineGeometry, material );
    // 把物体添加进场景中
    scene.add( mesh );

    const path = new THREE.Path();
    path.lineTo( -20, 0 );
    path.lineTo( -20, 30 );
    // path.quadraticCurveTo( 0, 1, 0.2, 1 );
    path.lineTo( 120, 30 );
    const points = path.getPoints();
    const geometryDot = new THREE.BufferGeometry().setFromPoints( points );
    const materialDot = new THREE.LineBasicMaterial( { color: 0x1890ff } );
    const line = new THREE.Line( geometryDot, materialDot );
    scene.add( line );

    const ctrlObj = {
      visible: true,
      transparent: false,
      opacity: 1,
      side: 'front',
      depthTest: true,
      depthWrite: true,
      alphaTest: 1
    }

    const control = new dat.GUI()
    const folder = control.addFolder('基础属性')
    folder.add(ctrlObj, 'visible').onChange(function (e) {
      cubeMaterial.visible = e
    })
    folder.add(ctrlObj, 'transparent').onChange(function (e) {
      cubeMaterial.transparent = e
    })
    folder.add(ctrlObj, 'opacity', 0, 1).onChange(function (e) {
      cubeMaterial.opacity = e
    })
    folder.add(ctrlObj, 'depthTest').onChange(function (e) {
      cubeMaterial.depthTest = e
    })
    folder.add(ctrlObj, 'depthWrite').onChange(function (e) {
      cubeMaterial.depthWrite = e
    })
    folder.add(ctrlObj, 'alphaTest', 0, 1).onChange(function (e) {
      cubeMaterial.alphaTest = e
    })
    folder
      .add(ctrlObj, 'side', ['front', 'back', 'dobule'])
      .onChange(function (e) {
        switch (e) {
          case 'front':
            cubeMaterial.side = THREE.FrontSide
            break
          case 'back':
            cubeMaterial.side = THREE.BackSide
            break
          case 'dobule':
            cubeMaterial.side = THREE.DoubleSide
            break
          default:
            break
        }

        cubeMaterial.needsUpdate = true
      })

    let linePoint ={ x: 0, y: 0, direct: 0 }
    
    const renderScene = () => {
      point(scene, linePoint)
      
      controls.update()
      requestAnimationFrame(renderScene)
      renderer.render(scene, camera)
    }

    renderScene()
  }, [])

  const point = (scene: THREE.Scene, point: {x: number, y: number, direct: number}) => {
    if (point.x < 0.5 && point.y < -5) return
    const speed = 0.5
    // 创建一个顶点缓冲几何体
    const geometry = new THREE.BoxGeometry(0, 0, 0 )
    // 点模型渲染几何体每个顶点
    const material = new THREE.PointsMaterial({
      color: 0xff040a,
      size: 2, //点大小
    });
    // 生成带有材质的物体
    const cube = new THREE.Points( geometry, material );
    if (point.direct === 0) {
      
      // 从左到右
      if (point.y === 20 || point.y % 20 === 0) {
        point.x = Number((point.x + speed).toFixed(2))
        cube.position.x = point.x
        cube.position.y = point.y
      }
      let flag = (point.x / 40) % 2

      if (point.x % 40 === 0 && point.y < 20 && flag === 1) {
        point.y = Number((point.y + speed).toFixed(2))
        cube.position.x = point.x
        cube.position.y = point.y
      }
      if (point.x % 40 === 0 && point.y > 0  && flag === 0) {
        point.y = Number((point.y - speed).toFixed(2))
        cube.position.x = point.x
        cube.position.y = point.y
      }

      if (point.x % 240 === 0 && point.y === 0) {
        point.direct = 1
      }
    }
    
    // 从右到左
    
    if (point.direct === 1) {

      if (point.y === -25 || point.y === -5 && point.x !== 240) {
        point.x = Number((point.x - speed).toFixed(2))
        cube.position.x = point.x
        cube.position.y = point.y
      }
      let flag = (point.x / 40) % 2
      if (point.x % 40 === 0 && point.y > -25 && flag === 0) {
        point.y = Number((point.y - 0.2).toFixed(1))
        cube.position.x = point.x
        cube.position.y = point.y
      }
      if (point.x % 40 === 0 && point.y < -5 && flag === 1) {
        point.y = Number((point.y + speed).toFixed(2))
        cube.position.x = point.x
        cube.position.y = point.y
      }
    }

    cube.position.z = 20
    // 把物体添加进场景中
    scene.add( cube );
  }

  const light = (scene: THREE.Scene) => {
    // 创建光源 聚光灯
    const spotlight = new THREE.SpotLight(0xffffff)
    spotlight.position.set(60, 500, 50)
    spotlight.castShadow = true
    spotlight.shadow.mapSize = new THREE.Vector2(4096, 4096)
    spotlight.shadow.camera.far = 1000
    spotlight.shadow.camera.near = 10
    scene.add(spotlight)

    // 环境光，没有特定的方向
    const ambientLight = new THREE.AmbientLight(0x444444)
    scene.add(ambientLight)
  }

  return <div className="App"></div>
}

export default App
